{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Introduction to Altair"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial will guide you through the process of creating visualizations in Altair. For details on installing Altair or its underlying philosophy, please see the [Altair Documentation](http://altair-viz.github.io/)\n",
    "\n",
    "Outline:\n",
    "\n",
    "- [The data](#The-data)\n",
    "- [The `Chart` object](#The-Chart-object)\n",
    "- [Data encodings and marks](#Data-encodings-and-marks)\n",
    "- [Data transformation: Aggregation](#Data-transformation:-Aggregation)\n",
    "- [Customizing your visualization](#Customizing-your-visualization)\n",
    "- [Publishing a visualization online](#Publishing-a-visualization-online)\n",
    "\n",
    "This tutorial is written in the form of a Jupyter Notebook; we suggest downloading the notebook and following along, executing the code yourself as we go. For creating Altair visualizations in the notebook, all that is required is to [install the package and its dependencies](https://altair-viz.github.io/installation.html) and import the Altair namespace:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import altair as alt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Data in Altair is built around the [Pandas Dataframe](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html).\n",
    "For the purposes of this tutorial, we'll start by importing Pandas and creating a simple `DataFrame` to visualize, with a categorical variable in column `a` and a numerical variable in column `b`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>a</th>\n",
       "      <th>b</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>C</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>C</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>C</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>D</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>D</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>D</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>E</td>\n",
       "      <td>8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>E</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>E</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   a  b\n",
       "0  C  2\n",
       "1  C  7\n",
       "2  C  4\n",
       "3  D  1\n",
       "4  D  2\n",
       "5  D  6\n",
       "6  E  8\n",
       "7  E  4\n",
       "8  E  7"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "data = pd.DataFrame({'a': list('CCCDDDEEE'),\n",
    "                     'b': [2, 7, 4, 1, 2, 6, 8, 4, 7]})\n",
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In Altair, every dataset should be provided as a `Dataframe`, or as a URL referencing an appropriate dataset (see [Defining Data](https://altair-viz.github.io/user_guide/data.html))."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Chart object\n",
    "\n",
    "The fundamental object in Altair is the ``Chart``. It takes the dataframe as a single argument:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "chart = alt.Chart(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fundamentally, a ``Chart`` is an object which knows how to emit a JSON dictionary representing the data and visualization encodings (see below), which can be sent to the notebook and rendered by the Vega-Lite JavaScript library.\n",
    "\n",
    "Here is what that JSON looks like for the current chart (since the chart is not yet complete, we turn off chart validation):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},\n",
       " 'data': {'name': 'data-347f1284ea3247c0f55cb966abbdd2d8'},\n",
       " '$schema': 'https://vega.github.io/schema/vega-lite/v4.0.0.json',\n",
       " 'datasets': {'data-347f1284ea3247c0f55cb966abbdd2d8': [{'a': 'C', 'b': 2},\n",
       "   {'a': 'C', 'b': 7},\n",
       "   {'a': 'C', 'b': 4},\n",
       "   {'a': 'D', 'b': 1},\n",
       "   {'a': 'D', 'b': 2},\n",
       "   {'a': 'D', 'b': 6},\n",
       "   {'a': 'E', 'b': 8},\n",
       "   {'a': 'E', 'b': 4},\n",
       "   {'a': 'E', 'b': 7}]}}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart.to_dict(validate=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point the specification contains only the data and the default configuration, but no visualization specification."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chart Marks\n",
    "\n",
    "Next we can decide what sort of *mark* we would like to use to represent our data.\n",
    "For example, we can choose the ``point`` mark to represent each data as a point on the plot:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-1\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-1\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"point\", \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart = alt.Chart(data).mark_point()\n",
    "chart"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is a visualization with one point per row in the data, though it is not a particularly interesting: all the points are stacked right on top of each other!\n",
    "To see how this affects the specification, we can once again examine the dictionary representation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},\n",
       " 'data': {'name': 'data-347f1284ea3247c0f55cb966abbdd2d8'},\n",
       " 'mark': 'point',\n",
       " '$schema': 'https://vega.github.io/schema/vega-lite/v4.0.0.json',\n",
       " 'datasets': {'data-347f1284ea3247c0f55cb966abbdd2d8': [{'a': 'C', 'b': 2},\n",
       "   {'a': 'C', 'b': 7},\n",
       "   {'a': 'C', 'b': 4},\n",
       "   {'a': 'D', 'b': 1},\n",
       "   {'a': 'D', 'b': 2},\n",
       "   {'a': 'D', 'b': 6},\n",
       "   {'a': 'E', 'b': 8},\n",
       "   {'a': 'E', 'b': 4},\n",
       "   {'a': 'E', 'b': 7}]}}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart.to_dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that now in addition to the data, the specification includes information about the mark type."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data encodings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to add *visual encodings* (or *encodings* for short) to the chart. A visual encoding specifies how a given data column should be mapped onto the visual properties of the visualization.\n",
    "Some of the more frequenty used visual encodings are listed here:\n",
    "\n",
    "* X: x-axis value\n",
    "* Y: y-axis value\n",
    "* Color: color of the mark\n",
    "* Opacity: transparency/opacity of the mark\n",
    "* Shape: shape of the mark\n",
    "* Size: size of the mark\n",
    "* Row: row within a grid of facet plots\n",
    "* Column: column within a grid of facet plots\n",
    "\n",
    "For a complete list of these encodings, see the [Encodings](https://altair-viz.github.io/user_guide/encoding.html) section of the documentation.\n",
    "\n",
    "Visual encodings can be created with the `encode()` method of the `Chart` object. For example, we can start by mapping the `y` axis of the chart to column `a`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-2\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-2\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"point\", \"encoding\": {\"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart = alt.Chart(data).mark_point().encode(y='a')\n",
    "chart"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is a one-dimensional visualization representing the values taken on by `a`.\n",
    "As above, we can view the JSON data generated for this visualization:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},\n",
       " 'data': {'name': 'data-347f1284ea3247c0f55cb966abbdd2d8'},\n",
       " 'mark': 'point',\n",
       " 'encoding': {'y': {'type': 'nominal', 'field': 'a'}},\n",
       " '$schema': 'https://vega.github.io/schema/vega-lite/v4.0.0.json',\n",
       " 'datasets': {'data-347f1284ea3247c0f55cb966abbdd2d8': [{'a': 'C', 'b': 2},\n",
       "   {'a': 'C', 'b': 7},\n",
       "   {'a': 'C', 'b': 4},\n",
       "   {'a': 'D', 'b': 1},\n",
       "   {'a': 'D', 'b': 2},\n",
       "   {'a': 'D', 'b': 6},\n",
       "   {'a': 'E', 'b': 8},\n",
       "   {'a': 'E', 'b': 4},\n",
       "   {'a': 'E', 'b': 7}]}}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart.to_dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is the same as above with the addition of the `'encoding'` key, which specifies the visualization channel (`y`), the name of the field (`a`), and the type of the variable (`nominal`).\n",
    "\n",
    "Altair is able to automatically determine the type of the variable using built-in heuristics. Altair and Vega-Lite support four primitive data types:\n",
    "\n",
    "<table>\n",
    "  <tr>\n",
    "    <th>Data Type</th>\n",
    "    <th>Code</th>\n",
    "    <th>Description</th>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>quantitative</td>\n",
    "    <td>Q</td>\n",
    "    <td>Numerical quantity (real-valued)</td>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>nominal</td>\n",
    "    <td>N</td>\n",
    "    <td>Name / Unordered categorical</td>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>ordinal</td>\n",
    "    <td>O</td>\n",
    "    <td>Ordered categorial</td>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>temporal</td>\n",
    "    <td>T</td>\n",
    "    <td>Date/time</td>\n",
    "  </tr>\n",
    "</table>\n",
    "\n",
    "You can set the data type of a column explicitly using a one letter code attached to the column name with a colon:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-3\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-3\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"point\", \"encoding\": {\"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alt.Chart(data).mark_point().encode(y='a:N')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The visualization can be made more interesting by adding another channel to the encoding: let's encode column `b` as the `x` position:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-4\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-4\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"point\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alt.Chart(data).mark_point().encode(\n",
    "    y='a',\n",
    "    x='b'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With two visual channels encoded, we can see the raw data points in the `DataFrame`. A different mark type can be chosen using a different `mark_*()` method, such as `mark_bar()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-5\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-5\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alt.Chart(data).mark_bar().encode(\n",
    "    alt.Y('a'),\n",
    "    alt.X('b')\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice, we have used a slightly different syntax for specifying the channels using classes (``alt.X`` and ``alt.Y``) passed as positional arguments. These classes allow additional arguments to be passed to each channel, as we will see below.\n",
    "\n",
    "Here are some of the more commonly used `mark_*()` methods supported in Altair and Vega-Lite; for more detail see [Marks](https://altair-viz.github.io/user_guide/marks.html) in the Altair documentation:\n",
    "\n",
    "|Method|\n",
    "|------|\n",
    "|``mark_area()``|\n",
    "|``mark_bar()``|\n",
    "|``mark_circle()``|\n",
    "|``mark_line()``|\n",
    "|``mark_point()``|\n",
    "|``mark_rule()``|\n",
    "|``mark_square()``|\n",
    "|``mark_text()``|\n",
    "|``mark_tick()``|"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data transformation: Aggregation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Altair and Vega-Lite also support a variety of built-in data transformations, such as aggregation. The easiest way to specify such aggregations is through a string-function syntax in the argument to the column name. For example, here we will plot not all the values, but a single point representing the mean of the x-values for a given y-value:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-6\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-6\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"point\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"aggregate\": \"mean\", \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alt.Chart(data).mark_point().encode(\n",
    "    y='a',\n",
    "    x='mean(b)'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Conceptually, this is equivalent to the following groupby operation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>b</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>a</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>C</th>\n",
       "      <td>4.333333</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>D</th>\n",
       "      <td>3.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>E</th>\n",
       "      <td>6.333333</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          b\n",
       "a          \n",
       "C  4.333333\n",
       "D  3.000000\n",
       "E  6.333333"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.groupby('a').mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "More typically, aggregated values are displayed using bar charts.\n",
    "Making this change is as simple as replacing `mark_point()` with `mark_bar()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-7\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-7\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"aggregate\": \"mean\", \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart = alt.Chart(data).mark_bar().encode(\n",
    "    y='a',\n",
    "    x='mean(b)'\n",
    ")\n",
    "chart"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As above, Altair's role in this visualization is converting the resulting object into an appropriate JSON dict.\n",
    "Here it is, leaving out the data for clarity:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},\n",
       " 'data': {'name': 'data-347f1284ea3247c0f55cb966abbdd2d8'},\n",
       " 'mark': 'bar',\n",
       " 'encoding': {'x': {'type': 'quantitative', 'aggregate': 'mean', 'field': 'b'},\n",
       "  'y': {'type': 'nominal', 'field': 'a'}},\n",
       " '$schema': 'https://vega.github.io/schema/vega-lite/v4.0.0.json',\n",
       " 'datasets': {'data-347f1284ea3247c0f55cb966abbdd2d8': [{'a': 'C', 'b': 2},\n",
       "   {'a': 'C', 'b': 7},\n",
       "   {'a': 'C', 'b': 4},\n",
       "   {'a': 'D', 'b': 1},\n",
       "   {'a': 'D', 'b': 2},\n",
       "   {'a': 'D', 'b': 6},\n",
       "   {'a': 'E', 'b': 8},\n",
       "   {'a': 'E', 'b': 4},\n",
       "   {'a': 'E', 'b': 7}]}}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart.to_dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that Altair has taken the string `'mean(b)'` and converted it to a mapping that includes `field`, `type`, and `aggregate`. The full shorthand syntax for the column names in Altair also includes the explicit type code separated by a column:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'type': 'quantitative', 'aggregate': 'mean', 'field': 'b'}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = alt.X('mean(b):Q')\n",
    "x.to_dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This shorthand is equivalent to spelling-out these properties by name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'type': 'quantitative', 'aggregate': 'average', 'field': 'b'}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = alt.X('b', aggregate='average', type='quantitative')\n",
    "x.to_dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is one benefit of using the Altair API over writing the Vega-Lite spec from scratch: valid Vega-Lite specifications can be created very succinctly, with less boilerplate code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Customizing your visualization"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To speed the process of data exploration, Altair (via Vega-Lite) makes some choices about default properties of the visualization.\n",
    "Altair also provides an API to customize the look of the visualization. For example, we can use the `X` object we saw above to override the default x-axis title:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-8\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-8\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"aggregate\": \"mean\", \"axis\": {\"title\": \"Mean of quantity b\"}, \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alt.Chart(data).mark_bar().encode(\n",
    "    y='a',\n",
    "    x=alt.X('mean(b)', axis=alt.Axis(title='Mean of quantity b'))\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The properties of marks can be configured by passing keyword arguments to the `mark_*()` methods; for example, any named HTML color is supported:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-9\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-9\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": {\"type\": \"bar\", \"color\": \"firebrick\"}, \"encoding\": {\"x\": {\"type\": \"quantitative\", \"aggregate\": \"mean\", \"axis\": {\"title\": \"Mean of quantity b\"}, \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alt.Chart(data).mark_bar(color='firebrick').encode(\n",
    "    y='a',\n",
    "    x=alt.X('mean(b)', axis=alt.Axis(title='Mean of quantity b'))\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, we can set properties of the chart such as width and height using the ``properties()`` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<div id=\"altair-viz-10\"></div>\n",
       "<script type=\"text/javascript\">\n",
       "  (function(spec, embedOpt){\n",
       "    const outputDiv = document.getElementById(\"altair-viz-10\");\n",
       "    const paths = {\n",
       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0?noext\",\n",
       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
       "    };\n",
       "\n",
       "    function loadScript(lib) {\n",
       "      return new Promise(function(resolve, reject) {\n",
       "        var s = document.createElement('script');\n",
       "        s.src = paths[lib];\n",
       "        s.async = true;\n",
       "        s.onload = () => resolve(paths[lib]);\n",
       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "      });\n",
       "    }\n",
       "\n",
       "    function showError(err) {\n",
       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
       "      throw err;\n",
       "    }\n",
       "\n",
       "    function displayChart(vegaEmbed) {\n",
       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
       "    }\n",
       "\n",
       "    if(typeof define === \"function\" && define.amd) {\n",
       "      requirejs.config({paths});\n",
       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
       "    } else if (typeof vegaEmbed === \"function\") {\n",
       "      displayChart(vegaEmbed);\n",
       "    } else {\n",
       "      loadScript(\"vega\")\n",
       "        .then(() => loadScript(\"vega-lite\"))\n",
       "        .then(() => loadScript(\"vega-embed\"))\n",
       "        .catch(showError)\n",
       "        .then(() => displayChart(vegaEmbed));\n",
       "    }\n",
       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"aggregate\": \"average\", \"axis\": {\"title\": \"Average of b\"}, \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"height\": 300, \"width\": 400, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}}, {\"mode\": \"vega-lite\"});\n",
       "</script>"
      ],
      "text/plain": [
       "alt.Chart(...)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart = alt.Chart(data).mark_bar().encode(\n",
    "    y='a',\n",
    "    x=alt.X('average(b)', axis=alt.Axis(title='Average of b'))\n",
    ").properties(\n",
    "    width=400,\n",
    "    height=300\n",
    ")\n",
    "\n",
    "chart"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As above, we can inspect how these configuration options affect the resulting Vega-lite specification:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},\n",
       " 'data': {'name': 'data-347f1284ea3247c0f55cb966abbdd2d8'},\n",
       " 'mark': 'bar',\n",
       " 'encoding': {'x': {'type': 'quantitative',\n",
       "   'aggregate': 'average',\n",
       "   'axis': {'title': 'Average of b'},\n",
       "   'field': 'b'},\n",
       "  'y': {'type': 'nominal', 'field': 'a'}},\n",
       " 'height': 300,\n",
       " 'width': 400,\n",
       " '$schema': 'https://vega.github.io/schema/vega-lite/v4.0.0.json',\n",
       " 'datasets': {'data-347f1284ea3247c0f55cb966abbdd2d8': [{'a': 'C', 'b': 2},\n",
       "   {'a': 'C', 'b': 7},\n",
       "   {'a': 'C', 'b': 4},\n",
       "   {'a': 'D', 'b': 1},\n",
       "   {'a': 'D', 'b': 2},\n",
       "   {'a': 'D', 'b': 6},\n",
       "   {'a': 'E', 'b': 8},\n",
       "   {'a': 'E', 'b': 4},\n",
       "   {'a': 'E', 'b': 7}]}}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart.to_dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To learn more about the various properties of chart objects, you can use Jupyter's help syntax:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[0;31mInit signature:\u001b[0m\n",
       "\u001b[0malt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mChart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mUndefined\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mencoding\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mUndefined\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mmark\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mUndefined\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mUndefined\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mheight\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mUndefined\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
       "\u001b[0;31mDocstring:\u001b[0m     \n",
       "Create a basic Altair/Vega-Lite chart.\n",
       "\n",
       "Although it is possible to set all Chart properties as constructor attributes,\n",
       "it is more idiomatic to use methods such as ``mark_point()``, ``encode()``,\n",
       "``transform_filter()``, ``properties()``, etc. See Altair's documentation\n",
       "for details and examples: http://altair-viz.github.io/.\n",
       "\n",
       "Attributes\n",
       "----------\n",
       "data : Data\n",
       "    An object describing the data source\n",
       "mark : AnyMark\n",
       "    A string describing the mark type (one of `\"bar\"`, `\"circle\"`, `\"square\"`, `\"tick\"`,\n",
       "     `\"line\"`, * `\"area\"`, `\"point\"`, `\"rule\"`, `\"geoshape\"`, and `\"text\"`) or a\n",
       "     MarkDef object.\n",
       "encoding : FacetedEncoding\n",
       "    A key-value mapping between encoding channels and definition of fields.\n",
       "autosize : anyOf(AutosizeType, AutoSizeParams)\n",
       "    Sets how the visualization size should be determined. If a string, should be one of\n",
       "    `\"pad\"`, `\"fit\"` or `\"none\"`. Object values can additionally specify parameters for\n",
       "    content sizing and automatic resizing. `\"fit\"` is only supported for single and\n",
       "    layered views that don't use `rangeStep`.  __Default value__: `pad`\n",
       "background : string\n",
       "    CSS color property to use as the background of visualization.\n",
       "\n",
       "    **Default value:** none (transparent)\n",
       "config : Config\n",
       "    Vega-Lite configuration object.  This property can only be defined at the top-level\n",
       "    of a specification.\n",
       "description : string\n",
       "    Description of this mark for commenting purpose.\n",
       "height : float\n",
       "    The height of a visualization.\n",
       "name : string\n",
       "    Name of the visualization for later reference.\n",
       "padding : Padding\n",
       "    The default visualization padding, in pixels, from the edge of the visualization\n",
       "    canvas to the data rectangle.  If a number, specifies padding for all sides. If an\n",
       "    object, the value should have the format `{\"left\": 5, \"top\": 5, \"right\": 5,\n",
       "    \"bottom\": 5}` to specify padding for each side of the visualization.  __Default\n",
       "    value__: `5`\n",
       "projection : Projection\n",
       "    An object defining properties of geographic projection.  Works with `\"geoshape\"`\n",
       "    marks and `\"point\"` or `\"line\"` marks that have a channel (one or more of `\"X\"`,\n",
       "    `\"X2\"`, `\"Y\"`, `\"Y2\"`) with type `\"latitude\"`, or `\"longitude\"`.\n",
       "selection : Mapping(required=[])\n",
       "    A key-value mapping between selection names and definitions.\n",
       "title : anyOf(string, TitleParams)\n",
       "    Title for the plot.\n",
       "transform : List(Transform)\n",
       "    An array of data transformations such as filter and new field calculation.\n",
       "width : float\n",
       "    The width of a visualization.\n",
       "\u001b[0;31mFile:\u001b[0m           ~/miniconda3/lib/python3.7/site-packages/altair/vegalite/v4/api.py\n",
       "\u001b[0;31mType:\u001b[0m           type\n",
       "\u001b[0;31mSubclasses:\u001b[0m     \n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "alt.Chart?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also read more in Altair's [Configuration](https://altair-viz.github.io/user_guide/configuration.html) documentation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Publishing a visualization online"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because Altair produces Vega-Lite specifications, it is relatively straightforward to export charts and publish them on the web as Vega-Lite plots.\n",
    "All that is required is to load the Vega-Lite javascript library, and pass it the JSON plot specification output by Altair.\n",
    "For convenience Altair provides a ``save()`` method, which will save any chart to HTML:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "chart.save('chart.html')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<!DOCTYPE html>\n",
      "<html>\n",
      "<head>\n",
      "  <style>\n",
      "    .error {\n",
      "        color: red;\n",
      "    }\n",
      "  </style>\n",
      "  <script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm//vega@5\"></script>\n",
      "  <script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm//vega-lite@4.0.0\"></script>\n",
      "  <script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm//vega-embed@6\"></script>\n",
      "</head>\n",
      "<body>\n",
      "  <div id=\"vis\"></div>\n",
      "  <script>\n",
      "    (function(vegaEmbed) {\n",
      "      var spec = {\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-347f1284ea3247c0f55cb966abbdd2d8\"}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"aggregate\": \"average\", \"axis\": {\"title\": \"Average of b\"}, \"field\": \"b\"}, \"y\": {\"type\": \"nominal\", \"field\": \"a\"}}, \"height\": 300, \"width\": 400, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0.json\", \"datasets\": {\"data-347f1284ea3247c0f55cb966abbdd2d8\": [{\"a\": \"C\", \"b\": 2}, {\"a\": \"C\", \"b\": 7}, {\"a\": \"C\", \"b\": 4}, {\"a\": \"D\", \"b\": 1}, {\"a\": \"D\", \"b\": 2}, {\"a\": \"D\", \"b\": 6}, {\"a\": \"E\", \"b\": 8}, {\"a\": \"E\", \"b\": 4}, {\"a\": \"E\", \"b\": 7}]}};\n",
      "      var embedOpt = {\"mode\": \"vega-lite\"};\n",
      "\n",
      "      function showError(el, error){\n",
      "          el.innerHTML = ('<div class=\"error\" style=\"color:red;\">'\n",
      "                          + '<p>JavaScript Error: ' + error.message + '</p>'\n",
      "                          + \"<p>This usually means there's a typo in your chart specification. \"\n",
      "                          + \"See the javascript console for the full traceback.</p>\"\n",
      "                          + '</div>');\n",
      "          throw error;\n",
      "      }\n",
      "      const el = document.getElementById('vis');\n",
      "      vegaEmbed(\"#vis\", spec, embedOpt)\n",
      "        .catch(error => showError(el, error));\n",
      "    })(vegaEmbed);\n",
      "\n",
      "  </script>\n",
      "</body>\n",
      "</html>"
     ]
    }
   ],
   "source": [
    "!cat chart.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that the chart specification is passed to the ``vegaEmbed`` library in the ``spec`` variable; the rest of the code is a template that is constant regardless of the chart."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can view the output in an iframe within the notebook (note that some online notebook viewers will not display iframes):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"400\"\n",
       "            height=\"200\"\n",
       "            src=\"chart.html\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x11557c550>"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display IFrame in IPython\n",
    "from IPython.display import IFrame\n",
    "IFrame('chart.html', width=400, height=200)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, you can use your web browser to open the file manually to confirm that it works: [chart.html](chart.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Learning More\n",
    "\n",
    "For more information on Altair, please refer to Altair's online documentation: http://altair-viz.github.io/\n",
    "\n",
    "You can also see some of the example plots listed in the [accompanying notebooks](01-Index.ipynb)."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
